from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from django.http import JsonResponse
from django.utils import timezone
# transaction模块，是django封装事务功能
from django.db import transaction

import json
from decimal import Decimal
from meiduo_mall.utils.views import LoginRequiredJSONMixin
from goods.models import SKU
from users.models import Address
from .models import OrderInfo,OrderGoods

# Create your views here.


# 结算订单
class OrderSettlementView(LoginRequiredJSONMixin, View):

    # GET + /settlement/
    def get(self, request):
        user = request.user

        # ======用户可选收货地址=======
        addresses = []
        addr_queryset = user.addresses.filter(is_deleted=False)
        for addr in addr_queryset:
            # addr: 收货地址Address模型类对象
            addresses.append({
                'id': addr.id,
                'province': addr.province.name,
                'city': addr.city.name,
                'district': addr.district.name,
                'place': addr.place,
                'mobile': addr.mobile,
                'receiver': addr.receiver
            })

        # ======用户选中的购物车商品=======
        # 注意：结算页面用户必然登陆，购物车必然已合并，所以此处只需要读取redis购物车数据即可
        conn = get_redis_connection('carts')
        # redis_carts = {b'1': b'3', b'2': b'5'}
        redis_carts = conn.hgetall('carts_%s'%user.id)
        # redis_selected = [b'2'] # 表明sku_id为2的商品被选中
        redis_selected = conn.smembers('selected_%s'%user.id)
        # 业务约定，结算页面中只返回选中的商品
        sku_ids = redis_carts.keys()
        skus = []
        for sku_id in sku_ids:
            # sku_id： 每一个sku商品的id
            if sku_id in redis_selected:  # 该if判断是否选中
                sku = SKU.objects.get(pk=sku_id) # pk=b'2' --> 自行转化pk=int(b'2')
                skus.append({
                    'id': sku.id,
                    'name': sku.name,
                    'default_image_url': sku.default_image.url,
                    'price': sku.price,
                    'count': int(redis_carts[sku_id])
                })

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'context': {
                'addresses': addresses,
                'skus': skus,
                'freight': 10.0
            },
        })



# 新建保存订单信息
class OrderCommitView(LoginRequiredJSONMixin, View):

    # POST + /orders/commit/
    def post(self, request):
        # 1、提取参数
        data = json.loads(request.body.decode())
        address_id = data.get('address_id')
        pay_method = data.get('pay_method')

        # 2、校验参数
        if not all([address_id, pay_method]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必要参数'})

        try:
            address = Address.objects.get(pk=address_id, is_deleted=False)
        except Address.DoesNotExist as e:
            return JsonResponse({'code': 400, 'errmsg': '地址无效'})

        if pay_method not in [
            OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']
        ]:
            return JsonResponse({'code': 400, 'errmsg': '支付方式无效'})

        # 3、业务数据处理 —— 操作OrderInfo和OrderGoods
        user = request.user
        # 获取购物车商品数据
        conn = get_redis_connection('carts')
        redis_carts = conn.hgetall('carts_%s'%user.id) # {b'1': b'3', b'2': b'5'}
        redis_selected = conn.smembers('selected_%s'%user.id) # [b'1', b'2']
        sku_ids = redis_carts.keys()

        cur_time = timezone.localtime()
        order_id = cur_time.strftime("%Y%m%d%H%M%S") + "%06d" % user.id

        # TODO: 难点：事务方式下单，包括乐观锁
        with transaction.atomic():
            # (1)、获取事务的一个保存点(回滚点)
            save_id = transaction.savepoint()

            # TODO: 新建订单OrderInfo对象保存数据库
            order = OrderInfo.objects.create(
                order_id=order_id, # 20200311100356000007 --> 时间字符串 + 6位字符格式化的用户id
                user=user,
                address=address,
                total_count=0, # 订单sku商品总数初始化为0，后续统计订单商品的时候再计算
                total_amount=0, # 订单总价初始化为0，后续统计订单商品的时候再计算
                freight=Decimal('10.0'), # 默认运费
                pay_method=pay_method,
                # status = 2 if pay_method==1 else 1
                status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method==OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
            )
            # TODO: 新建OrderGoods订单商品对象们保存数据库
            # 遍历购物车，根据选择的sku商品，新建订单商品表数据
            for sku_id in sku_ids:
                if sku_id in redis_selected: # 选中的sku商品id

                    while True:
                        # TODO: 乐观锁步骤1：获取旧库存和旧销量
                        sku = SKU.objects.get(pk=sku_id)
                        count = int(redis_carts[sku_id])# 购买量
                        old_stock = sku.stock
                        old_sales = sku.sales
                        # 判定库存够不够
                        if count > old_stock:
                            # (2)、回滚到保存点(撤销OrderInfo新建)
                            transaction.savepoint_rollback(save_id)
                            return JsonResponse({'code':400, 'errmsg': '"%s"库存不足'%sku.name})

                        # TODO: 乐观锁步骤2：计算新库存和销量
                        new_stock = old_stock - count
                        new_sales = old_sales + count

                        # TODO: 乐观锁步骤3：依据旧库存和销量查询原数据并更新
                        # result是update函数返回值为修改成功的数据库条数
                        # 注意：如果返回0表明没有数据更新，继而表明filter过滤出空查询集，继而表明旧数据发生改变，继而有说明有别的事务介入改变的旧数据
                        result = SKU.objects.filter(
                            pk=sku.id,
                            stock=old_stock,
                            sales=old_sales
                        ).update(stock=new_stock, sales=new_sales)

                        if result:
                            # 表明修改成功，说明没有别的事务介入，试一次有效的修改
                            break

                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=count,
                        price=sku.price
                    )

                    # 补充逻辑：每次处理sku的时候，需要更新order订单中的总数和总价
                    order.total_count += count
                    order.total_amount += sku.price * count
                    order.save()

            # 累加运费
            order.total_amount += order.freight
            order.save()

            # (3)、清除保存点
            transaction.savepoint_commit(save_id)

        # TODO: 删除购物车中下了单的sku商品
        # sku_ids里面是所有购物车的sku商品id，包括未勾选的。
        delete_sku_ids = [] # 记录即将被删除的商品id
        for sku_id in sku_ids:
            if sku_id in redis_selected:
                delete_sku_ids.append(sku_id)
        conn.hdel('carts_%s'%user.id, *delete_sku_ids)
        conn.srem('selected_%s'%user.id, *delete_sku_ids)

        # 4、构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'order_id': order_id
        })
























